#ifndef NETCOMPONENTS_H
#define NETCOMPONENTS_H

#include <ws2tcpip.h>
#include <winsock2.h>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <list>
#include <map>
#include <mutex>
#include <condition_variable>
#include <QString>

#define  NET_DES_NETTYPE     "netType"
#define  NET_DES_NODEID      "nodeID"
#define  NET_DES_NODETYPE    "nodeType"
#define  NET_DES_NODETIME    "nodeTime"
#define  NET_DES_NODEMSG     "nodeMsg"
#define  NET_DES_CHECKKEY    "checkKey"
#define  NET_DES_LINK        "linkStatus"

#define MAX_DATA_SEND_RECV_TIMEOUT_MS       (5000)      // 接收发送等待时间
#define NODECALLBACK_WAITTIME_MS            (500)       // 回调类数据返回等待时间
#define NODEPROC_WAITTIME_S                 (2)         // 线程等待超时时间
#define NODEPROC_TRTTIMES                   (10)        // 失败尝试次数

static std::string gstrNodeOffline = "NodeReadytoOffline";
static std::string gstrHeartEmptyMsg = "{\"type\":0}";
static std::string gstrComNodeStatus = "ComNodeStatus";

typedef enum _MSG_INFO_TYPE_
{
    MSG_INFO_TYPE_MSGDATA     = 0,     // 消息
    MSG_INFO_TYPE_STATUS_NEW  = 1,     // 节点新增
    MSG_INFO_TYPE_STATUS_OFF  = 2,     // 节点掉线
    MSG_INFO_TYPE_STATUS_EXIT = 3      // 节点退出
}MSG_INFO_TYPE;

typedef struct _MSG_INFO_
{
    MSG_INFO_TYPE   nType;      // 回调内容类型
    std::string     strID;      // 消息发送者ID
    std::string     strTime;    // 消息时间
    std::string     strMsg;     // 消息内容
}MSG_INFO;

typedef struct _NODE_MSG_INFO_
{
    std::string     strNode;    // 节点ID
    std::string     strMsg;     // 消息内容
}NODE_MSG_INFO;

typedef enum _CONNECT_TYPE_
{
    CONNECT_TYPE_UNKNOW = 0,    // 未知
    CONNECT_TYPE_LOGIN  = 1,    // 登录
    CONNECT_TYPE_HEART  = 2     // 心跳
}CONNECT_TYPE;

typedef struct _CLIENTINFO_
{
    SOCKET          nSocket;        // 套接字
    CONNECT_TYPE    nType;          // 连接类型
    std::string     strAddr;        // 连接地址
    std::string     strID;          // 节点ID
    std::string     strCheckKey;    // 校验口令
}CLIENTINFO;

typedef struct _NODE_INFO_
{
    MSG_INFO_TYPE   nType;      // 节点状态
    QString         strNodeID;  // 节点ID
}NODE_INFO;

// 全局初始化网络模块
bool TcpGlobalInit();
// 接收数据，并设置超时时间
int MyDataRecv(const SOCKET hSocket, char *pBuf, const int nLen, const int timeout);
// 发送数据，并设置超时时间
int MyDataSend(const SOCKET hSocket, const char *pBuf, const unsigned int nLen, const int timeout);
// 生成消息发送报文
bool createSendMsg(const std::list<MSG_INFO> &listMsg, std::string &strSendMsg);
// 解析消息接收报文
bool parseRecvMsg(const std::string &strRecvMsg, std::list<MSG_INFO> &listMsg);
// 消息格式化打印（html格式）
void getBrowserShowInfo(const MSG_INFO& stMsgInfo, QString &strInfo, QString &strShowMsg);

// 节点数据回调类
class NodeProcCallback
{
public:
    // 节点回调类构造函数
    NodeProcCallback();
    // 节点数据回调接口
    virtual void funcDataCallbackStatus(const MSG_INFO_TYPE nType, const std::string &strNodeID) = 0;
    // 回调解析后的数据
    virtual void funcDataCallbackMsg(const std::string &strNodeID, std::string &strMsgInfo) = 0;
    // 设置数据是否回调上层
    void setCallBackStatus(const bool bCallBack);
    // 调用层获取数据
    void getCallBackMsgWithCondition(std::list<MSG_INFO> &listMsgInfo);
    // 上层中止调用
    void setCallBackExit();
    // 获取已有消息
    int getRecvMsg(const int iMaxSize, std::list<MSG_INFO> &listMsg);

protected:
    bool                                m_bCallBackStatus;   // 数据是否回调上层状态
    bool                                m_bCallBackExit;     // 上层中止调用
    std::mutex                          m_mtxCallBackMsg;    // 待发送消息锁
    std::condition_variable             m_conCallBack;       // 待发送消息发送信号
    std::list<MSG_INFO>                 m_listMsgInfos;      // 交互消息数据
};

// 服务端子节点处理对象
class nodeProc
{
public:
    // 构造响应对象，配置回调对象
    nodeProc(NodeProcCallback *pCallBack = nullptr);
    // 析构资源
    ~nodeProc();
    // 初始化不同类型套接字
    bool initSocket(CONNECT_TYPE nType, SOCKET nProc);
    // 初始化处理对象
    bool init(const std::string &strNodeProcID);
    // 插入待发送数据，异步发送
    void insertMsg(const std::string &strMsg);

private:
    // 释放资源
    void release();
    // 心跳线程
    void threadHeartBeatAndRecv(char *pRecvData);
    // 数据发送线程
    void threadSendMsg();

private:
    NodeProcCallback*           m_pDataCallBack;    // 数据回调对象
    std::string                 m_strNodeProcID;    // 处理客户端标记ID,带有IP地址
    SOCKET                      m_nProc;            // 发送数据套接字，用于将上层的数据发送出去，单向
    SOCKET                      m_nHeart;           // 心跳数据套接字，用于接收数据并返回到上层，单向
    char*                       m_pRecvData;        // 心跳数据接收预留空间

    bool                        m_bExit;            // 线程退出信号
    std::thread                 m_threadHeartBeat;  // 响应客户端心跳线程
    std::thread                 m_threadSendMsg;    // 数据发送线程

    std::mutex                  m_mtxSendMsg;       // 待发送消息锁
    std::condition_variable     m_condSendMsg;      // 待发送消息发送信号
    std::list<std::string>      m_listSendMsg;      // 待发送数据，已组装好的报文
};

// 服务端
class nodeServer : public NodeProcCallback
{
public:
    // 服务端节点构建
    nodeServer();
    // 服务端节点析构
    virtual ~nodeServer();
    // 初始化服务端，配置口令、端口和侦听个数
    bool init(std::string strNodeKey, unsigned int uiPort, unsigned int uiListenSize);
    // 获取服务端状态
    bool getStatus();
    // 子处理节点状态回调
    void funcDataCallbackStatus(const MSG_INFO_TYPE nType, const std::string &strNodeID);
    // 子处理节点消息回调
    void funcDataCallbackMsg(const std::string &strNodeID, std::string &strMsgInfo);

private:
    // 释放资源
    void release();
    // 连接侦听线程
    void threadListenProc();
    // 节点管理线程
    void threadNodeMgr();
    // 数据调度线程
    void threadDispatch();
    // 从连接登录报文中解析套接字类型
    void getConnectType(std::string strMsg, CLIENTINFO &stClientInfo);
    // 生成应答报文
    bool getAckInfo(const bool bStatus, std::string &strAck);
    // 添加节点
    void addNodeProc(const CLIENTINFO &stClientInfo);

private:
    bool                                m_bExit;            // 线程退出信号
    bool                                m_bInitStatus;      // 初始化状态
    SOCKET                              m_sockServer;       // 侦听命令字
    std::string                         m_strNodeKey;       // 侦听口令

    // 客户端侦听和节点管理部分
    std::thread                         m_thListenProc;     // 侦听线程
    std::mutex                          m_mtxClients;       // 节点锁
    std::map<std::string, nodeProc*>    m_mapProcClients;   // 节点处理对象，key为 IP_ID 的形式;

    // 节点管理部分
    std::thread                         m_thNodeMgr;        // 节点管理线程
    std::mutex                          m_mtxNodes;         // 节点锁
    std::condition_variable             m_conNodes;         // 节点管理信号
    std::list<MSG_INFO>                 m_listNode;         // 掉线节点通知

    // 交互消息管理、转发和解析
    std::mutex                          m_mtxDispatch;      // 消息数据锁
    std::list<NODE_MSG_INFO>            m_listDispatch;     // 不同节点对应的所有消息数据
    std::condition_variable             m_conDispatch;      // 待转发解析消息发送信号
    std::thread                         m_thDispatch;       // 侦听线程
};

// 客户端
class nodeClient : public NodeProcCallback
{
public:
    // 客户端节点构建
    nodeClient();
    // 节点析构
    virtual ~nodeClient();
    // 设置本节点ID和口令
    void setParams(std::string strNodeId, std::string strNodeKey);
    // 初始化客户端，指定服务端IP、端口和口令，连接到服务端
    bool initAndConnect(std::string strServerIP, unsigned int uiPort, std::string strCheckKey);
    // 断开客户端已有连接
    bool disconnect();
    // 获取服务端状态
    bool getStatus();
    // 插入待发送数据，异步发送
    void insertMsg(MSG_INFO &stMsg);
    // 子处理节点状态回调(客户端不需要该函数支持)
    void funcDataCallbackStatus(const MSG_INFO_TYPE nType, const std::string &strNodeID){if( MSG_INFO_TYPE_STATUS_OFF == nType || strNodeID.empty())return;};
    // 子处理节点消息回调(客户端不需要该函数支持)
    void funcDataCallbackMsg(const std::string &strNodeID, std::string &strMsgInfo){if(strNodeID.empty() || strMsgInfo.empty())return;};

private:
    // 释放资源
    void release();
    // 发送断开信号
    void ready2close();
    // 关闭套接字
    void closeSocket();
    // 连接到客户端
    bool connect2Server(CONNECT_TYPE nType, SOCKET &nSock);
    // 生成连接报文
    bool getConnectMsg(CONNECT_TYPE nType, std::string &strMsg);
    // 解析返回连接报文
    bool parseConnectMsg(const std::string &strMsg);
    // 心跳线程
    void threadHeartBeatAndRecv(char *pRecvData);
    // 数据发送线程
    void threadSendMsg();

private:
    std::string                 m_strNodeID;        // 本客户端ID
    std::string                 m_strNodeKey;       // 本客户端口令
    unsigned int                m_nServerPort;      // 目标服务端端口
    std::string                 m_strServerIP;      // 目标服务端IP地址
    std::string                 m_strCheckKey;      // 目标服务端口令

    char*                       m_pRecvData;        // 接收线程缓冲区
    SOCKET                      m_nProc;            // 发送数据
    SOCKET                      m_nHeart;           // 接收客户端心跳数据

    bool                        m_bExit;            // 线程退出信号
    bool                        m_bInitStatus;      // 初始化状态
    std::thread                 m_threadHeartBeat;  // 响应客户端心跳线程
    std::thread                 m_threadSendMsg;    // 数据发送线程

    std::mutex                  m_mtxSendMsg;       // 待发送消息锁
    std::condition_variable     m_condSendMsg;      // 待发送消息发送信号
    std::list<MSG_INFO>         m_listSendMsg;      // 待发送消息队列
};

#endif // NETCOMPONENTS_H
